package com.hyt.it.ogt.ks.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.it.ogt.dict.service.IDictService;
import com.hyt.it.ogt.ks.config.ConfigManager;
import com.hyt.it.ogt.ks.config.KsBizRunTimeException;
import com.hyt.it.ogt.ks.enums.BizStatusCode;
import com.hyt.it.ogt.ks.enums.CandidateStatusCode;
import com.hyt.it.ogt.ks.enums.ExamConfigCode;
import com.hyt.it.ogt.ks.enums.WxLoginStatusEnms;
import com.hyt.it.ogt.ks.feign.FileClient;
import com.hyt.it.ogt.ks.feign.KwClient;
import com.hyt.it.ogt.ks.model.entity.CandidateProcess;
import com.hyt.it.ogt.ks.model.entity.MobileDeviceParam;
import com.hyt.it.ogt.ks.model.vo.AppMonitorStatusVO;
import com.hyt.it.ogt.ks.model.vo.ExamConfigVO;
import com.hyt.it.ogt.ks.model.vo.monitor.response.*;
import com.hyt.it.ogt.ks.service.ICandidateProcessService;
import com.hyt.it.ogt.ks.service.IExamCacheService;
import com.hyt.it.ogt.ks.service.ILogService;
import com.hyt.it.ogt.ks.service.IMobileService;
import com.hyt.it.ogt.ks.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author 刘恒活
 * @since 2021/7/23 14:47
 */
@Slf4j
@Service
public class MobileServiceImpl implements IMobileService {
    @Resource
    private KwClient kwClient;
    @Resource
    private ICandidateProcessService iCandidateProcessService;
    @Resource
    private IDictService iDictService;
    @Resource
    private FileClient fileClient;
    @Resource
    private ConfigManager configManager;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private IExamCacheService iExamCacheService;

    @Override
    public CandidateLoginVO login(String code, String examCode,Integer isScan) {
        code = StringUtil.trims(code.toUpperCase());
        String randomId = StringUtil.trims(examCode.toUpperCase());
        String json = kwClient.login(randomId, code,isScan);
        ApiResponse<CandidateLoginVO> apiResponse = ApiResponse.ConvertRet(json, CandidateLoginVO.class);
        if (apiResponse.getCode().intValue() != BizStatusCode.KW_SUCCESS_OK.getCode().intValue()) {
            log.info("请求考务接口kwClient.login失败{}", json);
            throw new KsBizRunTimeException(BizStatusCode.VIDEO_MONITOR_MOBILE_LOGIN_FAIL_CODE.getCode());
        }
        CandidateLoginVO candidateLoginVO = apiResponse.getData();
        int state = candidateLoginVO.getStatus();
        String examId = candidateLoginVO.getExamId();
        if (state == WxLoginStatusEnms.ZERO.getCode()) {
            QueryWrapper<CandidateProcess> wrapper = new QueryWrapper<CandidateProcess>();
            wrapper.lambda().eq(CandidateProcess::getCandidateId, candidateLoginVO.getUserId())
                    .eq(CandidateProcess::getExamId, examId);
            CandidateProcess candidateProcess = iCandidateProcessService.getOne(wrapper);
            if (candidateProcess == null) {
                // 4 ：未登陆
                state = WxLoginStatusEnms.FOUR.getCode();
            } else {
                Integer examStatus = candidateProcess.getExamStatus().intValue();
                if (examStatus == CandidateStatusCode.UN_LOGIN.getCode().intValue()) {
                    // 4 ：未登陆
                    state = WxLoginStatusEnms.FOUR.getCode();
                } else if (examStatus >= CandidateStatusCode.COMMIT.getCode().intValue()) {
                    //2: 考试已结束
                    state = WxLoginStatusEnms.TWO.getCode();
                }
            }
        }
        candidateLoginVO.setStatus(state);
        candidateLoginVO.setStatus(state);
        if (state > WxLoginStatusEnms.ZERO.getCode()) {
            candidateLoginVO.setExamId(null);
            candidateLoginVO.setUserId(null);
            // 数据字典
            candidateLoginVO.setMsg(iDictService.getLabel(Constants.DICT_APP_LOGIN_STATE, "" + state));
        }
        return candidateLoginVO;
    }

    @Override
    public MonitorSignVO getSign(String examId, String userId) {
        CandidateProcess one = iCandidateProcessService.lambdaQuery().eq(CandidateProcess::getExamId, examId)
                .eq(CandidateProcess::getCandidateId, userId).eq(CandidateProcess::getDelFlag, false).one();
        if(one.getExamStatus() != null && (one.getExamStatus().intValue() >= CandidateStatusCode.COMMIT.getCode().intValue())
                || one.getExamStatus().intValue() == CandidateStatusCode.UN_LOGIN.getCode().intValue()) {
            // 完成考试不给签名
            return null;
        }

        String json = kwClient.getSign(examId, userId, 1);
        ApiResponse<MonitorSignVO> apiResponse = ApiResponse.ConvertRet(json, MonitorSignVO.class);
        if (apiResponse.getCode().intValue() != BizStatusCode.KW_SUCCESS_OK.getCode().intValue()) {
            log.info("请求考务接口kwClient.getSign失败{}", json);
            throw new KsBizRunTimeException(BizStatusCode.VIDEO_MONITOR_GET_SIGN_FAIL_CODE.getCode());
        }
        CandidateProcess candidateProcess = new CandidateProcess();
        // 0：未登录，1：已登录
        candidateProcess.setMobileLoginStatus(1);
        LambdaQueryWrapper<CandidateProcess> query = Wrappers.lambdaQuery();
        query.eq(CandidateProcess::getCandidateId, userId);
        query.eq(CandidateProcess::getExamId,examId);
        // 记录app等登录状态
        iCandidateProcessService.update(candidateProcess, query);
        apiResponse.getData().setStrRoomID(apiResponse.getData().getRoomId());
        return apiResponse.getData();
    }

    @Override
    public CandidateValidPhoto getCandidateInfo(String userId) {
        String json = kwClient.selectCandidateBaseInfoForMonitor(userId);
        log.debug("获取考生照片列表：{}", json);
        ApiResponse<CandidateValidPhoto> apiResponse = ApiResponse.ConvertRet(json, CandidateValidPhoto.class);
        if (apiResponse.getCode().intValue() != BizStatusCode.KW_SUCCESS_OK.getCode().intValue()) {
            throw new KsBizRunTimeException(BizStatusCode.VIDEO_MONITOR_MOBILE_GET_PHOTO_FAIL_CODE.getCode());
        }
        List<CandidateInfoDynamicVO> dynamicFieldList = apiResponse.getData().getDynamicFieldList();
        if(dynamicFieldList != null) {
            dynamicFieldList.forEach(item -> {
                item.setCode(item.getTitle());
            });
        }
        return apiResponse.getData();
    }

    @Override
    public boolean faceMatch(String examId, String userId, String imageUrlA, String imageUrlB, String imageBase64) {
        log.info("==========faceMatch请求参数：【{}】、【{}】、【{}】、【{}】", userId, imageUrlA, imageUrlB);
        // true表示验证通过，false表示验证不通过
        boolean flag = true;
        if (StringUtils.isNotBlank(imageBase64)) {
            String path = fileClient.uploadFile(Base64Utils.string2Byte(imageBase64), "jpg");
            String imageUrlC = configManager.getNginxPath() + path;
            log.info("==========faceMatch（imageUrlC）：【{}】", imageUrlC);
            int count = 0;
            if (StringUtils.isNotBlank(imageUrlA)) {
                count++;
                flag = compareFace(examId, userId, imageUrlA, imageUrlC);
            }
            if (StringUtils.isNotBlank(imageUrlB)) {
                count++;
                flag = compareFace(examId, userId, imageUrlB, imageUrlC);
            }
            log.info("==========faceMatch比对结果：{},{},{}", userId, flag, count);
            // 必须至少有一次比对
            if (count == 0) {
                flag = false;
                //iLogService.faceMatchLog(examId, userId);
            } else {
                // 只要有比对就更新结果
                kwClient.updateMobileMonitorPhoto(examId, userId, imageUrlA, imageUrlB, path, flag ? 1 : 2);
            }
        }
        return flag;
    }

    @Override
    public boolean faceMatchForWX(String examId, String userId, String imageUrlA, String imageUrlB, String imageUrlC) {
        log.info("==========faceMatch请求参数：【{}】、【{}】、【{}】、【{}】、【{}】", examId, userId, imageUrlA, imageUrlB, imageUrlC);
        // true表示验证通过，false表示验证不通过
        boolean flag = true;
        // 是否有比对
        boolean hadCompare = false;
        if (StringUtils.isNotBlank(imageUrlC)) {
            // 前端只会传一张照片，要么imageUrlA要么imageUrlB
            String url = StringUtils.isNotBlank(imageUrlA) ? imageUrlA : imageUrlB;
            if (StringUtils.isNotBlank(url)) {
                //
                hadCompare = true;
                flag = compareFace(examId, userId, url, imageUrlC);
            }
            // 必须至少有一次比对
            if (!hadCompare) {
                flag = false;
                //iLogService.mobilePhotoMatchLog(examId, userId);
            } else {
                // 只要有比对就更新结果
                kwClient.updateMobileMonitorPhoto(examId, userId, imageUrlA, imageUrlB, imageUrlC, flag ? 1 : 2);
            }
        }
        return flag;
    }

    private boolean compareFace(String examId, String userId, String imageUrlA, String imageUrlB) {
        String res = kwClient.faceCompareByUrl(examId, userId, imageUrlA, imageUrlB);
        ApiResponse apiResponse = ApiResponse.ConvertRet(res, ApiResponse.class);
        if (apiResponse.getCode().intValue() == BizStatusCode.KW_SUCCESS_OK.getCode().intValue()) {
            return true;
        }
        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CandidateMonitorStartVO startMonitor(String userId, String examId) {
        CandidateMonitorStartVO vo = new CandidateMonitorStartVO();
        vo.setImageUrl(configManager.getAppImageUrl());
        vo.setTimeInterval(configManager.getAppTimeInterval());
        List<ExamConfigVO> examConfigCache = iExamCacheService.getExamConfigCache(examId);
        ExamConfigVO examConfigVO1 = examConfigCache.stream().filter(config -> ExamConfigCode.EXAM_CONFIG_CODE_LOCATION_REQUIREMENTS.getCode().equalsIgnoreCase(config.getConfigCode())).findFirst().orElse(null);
        if(Objects.nonNull(examConfigVO1)) {
            vo.setRequirement(examConfigVO1.getConfigValue());
        }
        CandidateProcess candidateProcess = new CandidateProcess();
        // 0：未登录，1：已登录
        candidateProcess.setMobileLoginStatus(1);
        LambdaQueryWrapper<CandidateProcess> query = Wrappers.lambdaQuery();
        query.eq(CandidateProcess::getExamId, examId).eq(CandidateProcess::getCandidateId, userId);
        // 记录app等登录状态
        iCandidateProcessService.update(candidateProcess, query);
        return vo;
    }

    @Override
    public CandidateExamStateVO getMonitorState(MobileDeviceParam param) {
        CandidateExamStateVO vo = CandidateExamStateVO.builder().state(0).build();
        // ====== 收集移动端设置 =======
        String muteKey = this.getKey(param.getExamId(), param.getUserId(), 0);
        if (param.getMuteState() != null) {
            String preMuteNoticeKey = this.getKey(param.getExamId(), param.getUserId(), 1);
            // 非静音模式，要通知考试端
            if (param.getMuteState().booleanValue() == false) {
                this.setCacheForMobile(muteKey);
                // 上次通知时间
                if (!redisTemplate.hasKey(preMuteNoticeKey)) {
                    this.setCacheForMobile(preMuteNoticeKey);
                }
            } else {
                // 开启静音模式，不需要通知考试端
                redisTemplate.delete(muteKey);
                redisTemplate.delete(preMuteNoticeKey);
            }
        }
        String offLineKey = this.getKey(param.getExamId(), param.getUserId(), 2);
        this.setCacheForMobile(offLineKey);
        // ====== 收集移动端设置 =======
        QueryWrapper<CandidateProcess> wrapper = Wrappers.query();
        wrapper.lambda().eq(CandidateProcess::getCandidateId, param.getUserId())
                .eq(CandidateProcess::getExamId, param.getExamId());
        CandidateProcess candidateProcess = iCandidateProcessService.getOne(wrapper);
        if (candidateProcess.getExamStatus().intValue() >= CandidateStatusCode.COMMIT.getCode().intValue()) {
            vo.setState(1);
        }
        ApiResponse<Boolean> api = kwClient.getMobilePlayingStatus(param.getExamId(), param.getUserId());
        vo.setIsPlaying(api.getData());
        return vo;
    }

    private String getKey(String examId, String userId, int type) {
        if (type == 0) {
            return KeyDefineCommon.getMobileDeviceMuteKey(examId, userId);
        } else if (type == 1) {
            return KeyDefineCommon.getMobileDeviceMuteNoticeKey(examId, userId);
        } else if (type == 2) {
            return KeyDefineCommon.getMobileDeviceOffLineKey(examId, userId);
        }
        return null;
    }

    private void setCacheForMobile(String key) {
        redisTemplate.opsForValue().set(key, DateUtil.getSecondByNow(), 12, TimeUnit.HOURS);
    }

    /**
     * 获取考试当中的考生状态
     * @param examId
     * @param candidateId
     * @return
     */
    @Override
    public CandidateExamStateVO getAppMonitorStatus(String examId,String candidateId){
        String resStr = kwClient.getAppPlayStatus(candidateId, examId);
        ApiResponse<AppMonitorStatusVO> res = ApiResponse.ConvertRet(resStr,AppMonitorStatusVO.class);
        if(Objects.nonNull(res) && BizStatusCode.KW_SUCCESS_OK.getCode().equals(res.getCode()) && Objects.nonNull(res.getData()) && res.getData().getIsPlaying()){
            //考生有app的直播
            return CandidateExamStateVO.builder().state(0).isPlaying(Boolean.TRUE).roomName(res.getData().getRoomName()).build();
        }
        return CandidateExamStateVO.builder().state(1).isPlaying(Boolean.FALSE).build();
    }
}
